package se.webbzon.boltzmann.minigame;

import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.awt.Rectangle;
import java.util.SortedSet;
import java.util.TreeSet;

import se.webbzon.boltzmann.minigame.effect.SandBoxSpark;
import se.webbzon.boltzmann.minigame.sandbox.DefaultSandBoxItem;
import se.webbzon.boltzmann.minigame.sandbox.SandBox;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxIcon;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxObject;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxScore;
import se.webbzon.boltzmann.resource.Resources;
import se.webbzon.oschi01.font.HorizontalAlignment;
import se.webbzon.oschi01.font.VerticalAlignment;
import se.webbzon.oschi01.resourceset.ResourceSet;
import se.webbzon.oschi01.utilities.StringUtils;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldcamera.WorldCameraPane;
import se.webbzon.oschi01.worldobject.WorldObject;

public class ScoreBar extends DefaultSandBoxItem {
	
	// The 2-dimensional depth of this score bar
	private static final int depth2d = 10;
	
	// The image set of this score bar
	private static final ResourceSet<Image> 
			stars = Resources.getImageSet("minigame_star"), 
			vBar = Resources.getImageSet("minigame_score_bar_vertical"), 
			hBar = Resources.getImageSet("minigame_score_bar_horizontal");
	
	// The font of the score
	private static final Font font = Resources.getFont("larabie_plain").deriveFont(16f);
	
	// The size of this score bar
	private static final int vWidth = 16, vHeight = 256;
	private static final int hWidth = vHeight, hHeight = vWidth;
	
	// The size of the stars
	private static final int starWidth = 24, starHeight = 24;
	
	// The maximal number of checkpoints for this score bar
	private static final int maxCheckpoints = 5;
	
	// The checkpoints of this score bar
	private final SortedSet<Integer> checkpoints;
	
	// The alignment of this score bar
	private HorizontalAlignment hAlign;
	private VerticalAlignment vAlign;
	
	// Whatever the score bar is vertical
	private boolean vertical;
	
	// The sand box to which this score bar is attached
	private SandBox box;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public ScoreBar(boolean vertical) {
		hAlign = HorizontalAlignment.LEFT;
		vAlign = VerticalAlignment.TOP;
		checkpoints = new TreeSet<Integer>();
		setInstance(new Instance());
		setVertical(vertical);
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Adds a checkpoint to this score bar. **/
	public boolean addCheckpoint(int points) {
		if (checkpoints.size() < maxCheckpoints)
			return checkpoints.add(points);
		else
			return false;
	}
	
	/** Removes a checkpoint from this score bar. **/
	public boolean removeCheckpoint(int points) {
		return checkpoints.remove(points);
	}
	
	/** Returns the horizontal alignment of this score bar. **/
	public HorizontalAlignment getHorizontalAlignment() {
		return hAlign;
	}

	/** Sets the horizontal alignment of this score bar. **/
	public void setHorizontalAlignment(HorizontalAlignment hAlign) {
		this.hAlign = hAlign;
		SandBox box = this.box;
		if (box != null)
			updatePosition(box);
	}

	/** Returns the vertical alignment of this score bar. **/
	public VerticalAlignment getVerticalAlignment() {
		return vAlign;
	}

	/** Sets the vertical alignment of this score bar. **/
	public void setVerticalAlignment(VerticalAlignment vAlign) {
		this.vAlign = vAlign;
		SandBox box = this.box;
		if (box != null)
			updatePosition(box);
	}
	
	/** Sets whatever this score bar is vertical. **/
	public void setVertical(boolean vertical) {
		this.vertical = vertical;
		setSize(vertical ? vWidth : hWidth, vertical ? vHeight : hHeight);
		SandBox box = this.box;
		if (box != null)
			updatePosition(box);
	}

	/** Returns true if this score bar is vertical. **/
	public boolean isVertical() {
		return vertical;
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Updates the position of the score bar so that it is positioned
	 * correctly inside the provided sand box. **/
	private void updatePosition(SandBox box) {
		Rectangle r = new Rectangle();
		box.getBoundary(r);
		final double x, y;
		switch (hAlign) {
		case LEFT: 		x = 4 + getWidth() / 2; break;
		case CENTER: 	x = r.getWidth(); break;
		default: 		x = r.getWidth() - getWidth() / 2 - 4; break;
		}
		switch (vAlign) {
		case TOP: 		y = 4 + getHeight() / 2; break;
		case MIDDLE: 	y = r.getHeight(); break;
		default: 		y = r.getHeight() - getHeight() / 2 - 4; break;
		}
		setPosition(x, y);
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	private final class Instance extends SandBoxObject {
		
		private int atCheckpoint;

		public Instance() {
			super(WorldObject.onPostDraw + WorldObject.onStep, depth2d);
		}

		@Override public void onAttach(SandBox box) {
			atCheckpoint = 0;
			ScoreBar.this.box = box;
			updatePosition(box);
		}

		@Override public void onDetach(SandBox box) {
			ScoreBar.this.box = null;
		}

		@Override public SandBoxIcon createIcon() {
			return null;
		}
		
		@Override public void onStep(WorldArea world) {
			final int max;
			SandBoxScore score = box.getScore();
			if (score != null && !checkpoints.isEmpty()) {
				max = checkpoints.last();
				int i = 1;
				for (Integer cp : checkpoints) {
					if (cp <= score.getPoints() && i > atCheckpoint) {
						atCheckpoint = i;
						float p = cp / (float) max;
						double xy = vertical ? getY() + getHeight() * (0.5 - p) : getX() + getWidth() * (p - 0.5);
						double scx = vertical ? getX() : xy; 
						double scy = vertical ? xy : getY();
						box.addItem(new SandBoxSpark(i-1,scx,scy));
					}
					i++;
				}
			}
		}

		@Override public void onPostDraw(WorldArea world, WorldCameraPane hud) {
			final int w = (int) getWidth(), h = (int) getHeight();
			final int xc = (int) getX(), yc = (int) getY();
			final int x = xc - w/2, y = yc - h/2;
			
			ResourceSet<Image> bar = isVertical() ? vBar : hBar;
			hud.drawImage(bar.get(1), x, y, w, h);
			
			// Return if no score is attached
			SandBoxScore score = box.getScore();
			if (score == null) {
				hud.drawImage(bar.get(0), x, y, w, h);
			} else {
				final long points = score.getPoints();
				final int max;
				if (checkpoints.isEmpty())
					max = 0;
				else
					max = checkpoints.last(); 
				float p = Math.min(1, points / (float) max);
				double fw = vertical ? getWidth() : p * getWidth();
				double fh = vertical ? p * getHeight() : getHeight();
				hud.setColor(Color.getHSBColor(p, p, 1f));
				hud.fillRect(x, y + (int) (getHeight() - fh), (int) fw, (int) fh);
				hud.drawImage(bar.get(0), x, y, w, h);
				
				int sw = starWidth, sh = starHeight, i = 1;
				for (Integer cp : checkpoints) {
					p = cp / (float) max;
					double xy = vertical ? y + getHeight() * (1 - p) : x + getWidth() * p;
					int scx = vertical ? xc : (int) xy; 
					int scy = vertical ? (int) xy : yc;
					int index = points >= cp ? i : 0;
					hud.drawImage(stars.get(index), scx - sw/2, scy - sh/2, sw, sh);
					i++;
				}
				
				final int tx, ty;
				
				switch (hAlign) {
				case LEFT: 		tx = x + w + 16; break;
				case CENTER: 	tx = x + w/2; break;
				default: 		tx = x - 16; break;
				}
				switch (vAlign) {
				case TOP: 		ty = y; break;
				case MIDDLE: 	ty = y + h/2; break;
				default: 		ty = y + h; break;
				}
				
				String str = StringUtils.leadPadding(Long.toString(points), 6, '0') + "p";
				hud.setFont(font);
				hud.setHAlignment(hAlign);
				hud.setVAlignment(vAlign);
				hud.setColor(Color.black);
				hud.drawString(str, tx, ty);
				hud.setColor(Color.green);
				hud.drawString(str, tx-1, ty-1);
			}
		}
		
	}

}
